package dynload

/*
#include "../c/dynload/dynload.h"
#include "bridge/_bridge_.h"
#include <stdlib.h>
void * go_malloc(unsigned long size){
    return malloc(size);
}
*/
import "C"
import (
	"gitee.com/yonoz/go_dyncall/model"
	"runtime"
	"unsafe"
)

type DLLib struct {
	lib  *C.DLLib
	free bool
}
type DLSyms struct {
	symbols *C.DLSyms
	free    bool
}
type dllPointer struct {
	ptr unsafe.Pointer
}

func (pointer dllPointer) GetPointer() unsafe.Pointer {
	return pointer.ptr
}

func DlLoadLibrary(libPath string) *DLLib {
	path := C.CString(libPath)
	defer C.free(unsafe.Pointer(path))
	lib := C.dlLoadLibrary(path)
	if lib == nil {
		return nil
	}
	dlLib := &DLLib{lib: lib}
	runtime.SetFinalizer(dlLib, DlFreeLibrary)
	return dlLib
}
func DlFreeLibrary(dlLib *DLLib) {
	if dlLib == nil || dlLib.free {
		return
	}
	dlLib.free = true
	C.dlFreeLibrary(dlLib.lib)
}
func DlFindSymbol(dlLib *DLLib, name string) model.Pointer {
	if dlLib == nil {
		return nil
	}
	functionName := C.CString(name)
	defer C.free(unsafe.Pointer(functionName))
	return &dllPointer{ptr: C.dlFindSymbol(dlLib.lib, functionName)}
}
func DlGetLibraryPath(lib *DLLib, bufferSize int) string {
	if lib == nil {
		return ""
	}
	maxSize := C.ulong(bufferSize)
	ptr := C.go_malloc(maxSize)
	chars := (*C.char)(ptr)
	defer C.free(ptr)
	maxSize_ := C.int(bufferSize)
	size := C.dlGetLibraryPath(lib.lib, chars, maxSize_)
	return C.GoStringN(chars, size)
}
func DlSymsInit(libPath string) *DLSyms {
	path := C.CString(libPath)
	defer C.free(unsafe.Pointer(path))
	symbols := C.dlSymsInit(path)
	if symbols == nil {
		return nil
	}
	dlSyms := &DLSyms{symbols: symbols}
	runtime.SetFinalizer(dlSyms, DlSymsCleanup)
	return dlSyms
}
func DlSymsCount(dlSysms *DLSyms) int {
	if dlSysms == nil {
		return 0
	}
	count := C.dlSymsCount(dlSysms.symbols)
	return int(count)
}
func DlSymsName(dlSysms *DLSyms, index int) string {
	if dlSysms == nil {
		return ""
	}
	i := C.int(index)
	name := C.dlSymsName(dlSysms.symbols, i)
	if name == nil {
		return ""
	}
	return C.GoString(name)
}
func DlSymsNameFromValue(dlSysms *DLSyms, pointer model.Pointer) string {
	if dlSysms == nil || pointer == nil || pointer.GetPointer() == nil {
		return ""
	}
	name := C.dlSymsNameFromValue(dlSysms.symbols, pointer.GetPointer())
	if name == nil {
		return ""
	}
	return C.GoString(name)
}
func DlSymsCleanup(dlSysms *DLSyms) {
	if dlSysms == nil || dlSysms.free {
		return
	}
	dlSysms.free = true
	C.dlSymsCleanup(dlSysms.symbols)
}
